﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Framework.Dependency;
using Framework.Exceptions;
using Interfaces.Enums;
using Interfaces.Infrastructure;
using Interfaces.Models;
using Microsoft.Practices.Unity;

namespace Framework.Security
{
    //todo change hardcoded position for fields
    public class CustomSessionTiketSerializer: ITiketSerializer
    {
        private const char UserFieldSplitter = ';';
        private const char RoleInfoSplitter = ':';
        private const char RoleFieldSplitter = ',';
        private const char Splitter = '*';
        
        private readonly IEncriptionService _service;

        public CustomSessionTiketSerializer(IEncriptionService service)
        {
            _service = service;
        }

        public string Serialize(ISession model)
        {
            if(model == null)
                throw new ArgumentNullException("model");
            
            var builder = new StringBuilder();

            var sessionInfo = string.Format("{0}", model.Id);
            
            var userInfo = string.Format("{1}{0}{2}{0}{3}{0}{4}", UserFieldSplitter,
                model.User.Id,
                model.User.UserName,
                model.User.Email,
                model.User.Password);

            var roleInfoBuilder = new StringBuilder();

            foreach (var role in model.User.Roles)
            {
                roleInfoBuilder.Append(role.Id);
                roleInfoBuilder.Append(RoleFieldSplitter);
                roleInfoBuilder.Append(role.Type);
                roleInfoBuilder.Append(RoleInfoSplitter);
            }

            builder.Append(sessionInfo);
            builder.Append(Splitter);
            builder.Append(userInfo);
            builder.Append(Splitter);
            builder.Append(roleInfoBuilder);

            return _service.Encript(builder.ToString());
        }

        public ISession Deserialize(string ticket)
        {

            ISession session = null;

            try
            {
                if (string.IsNullOrEmpty(ticket))
                    throw new ArgumentException("ticket should't be empty");

                var decriptString = _service.Decript(ticket);

                if (string.IsNullOrEmpty(decriptString))
                    throw new SerializationException("token not be deserialized");
                
                var sessionWithusersWithRoles = decriptString.Split(Splitter);
                var sessionInfo = sessionWithusersWithRoles[0];
                var usersInfo = sessionWithusersWithRoles[1].Split(UserFieldSplitter);
                var roleInfo = sessionWithusersWithRoles[2].Split(new []{RoleInfoSplitter},StringSplitOptions.RemoveEmptyEntries);

                var logicUser = DependencyManager.Container.Resolve<IUser>();

                logicUser.Id = Guid.Parse(usersInfo[0]);
                logicUser.UserName = usersInfo[1];
                logicUser.Email = usersInfo[2];
                logicUser.Password = usersInfo[3];
                logicUser.Roles = SerializeRoles(roleInfo);

                session = DependencyManager.Container.Resolve<ISession>();

                session.Id = Guid.Parse(sessionInfo);
                session.User = logicUser;

            }
            catch (Exception exception)
            {
                if(ExceptionHandlingManager.HandleException(exception, PolicyNames.SerializationPolicy))
                    throw ;
            }

            return session;
        }

        private ICollection<IRole> SerializeRoles(IEnumerable<string> roleInfo)
        {
            var rolesList = new List<IRole>();
            foreach (var roleFields in roleInfo.Select(roleInf => roleInf.Split(RoleFieldSplitter)))
            {
                RoleType type;
                if (!Enum.TryParse(roleFields[1], out type))
                    throw new SerializationException(string.Format("Value {0} can not be serialized", roleFields[1]));

                var logicRole = DependencyManager.Container.Resolve<IRole>();

                logicRole.Id = Guid.Parse(roleFields[0]);
                logicRole.Type = type;
                
                rolesList.Add(logicRole);
            }
            return rolesList;
        }
    }
}
